package com.mingyuean.demo.mybatis.binding;

import cn.hutool.core.lang.ClassScanner;
import com.mingyuean.demo.mybatis.session.Configuration;
import com.mingyuean.demo.mybatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author MingYueAn
 * <p>  映射器注册表，记录在 MyBatis Configuration 中注册过的 Mapper 接口
 * <p>  2023/3/14 13:45
 * @version: 1.0
 */
public class MapperRegistry {

    private static final Logger log = LoggerFactory.getLogger(MapperRegistry.class);

    /**
     *  MyBatis Configuration 对象
     */
    private final Configuration configuration;

    /**
     * 构造方法，创建一个 MapperRegistry 对象
     * @param configuration MyBatis Configuration 对象
     */
    public MapperRegistry(Configuration configuration) {
        this.configuration = configuration;
    }

    /**
     * 存放映射器代理的集合
     * key--->类 value--->映射代理工厂
     */
    private final Map<Class<?>, MapperProxyFactory<?>> mapperProxyFactoryMap = new HashMap<>();

    /**
     * 通过包名扫描 Mapper 接口，并将这些接口添加到 MyBatis 的 Configuration 中
     *
     * @param packageName 指定的包名
     */
    public void addMappers(String packageName) {
        // 通过包名扫描
        final Set<Class<?>> mapperClassSet = ClassScanner.scanPackage(packageName);
        // 循环遍历
        for (Class<?> mapperClass : mapperClassSet) {
            addMapper(mapperClass);
        }
    }

    /**
     * 将mapperClass添加到 mapperProxyFactoryMap
     *
     * @param mapperClass 需要添加的mapperClass
     */
    public <T> void addMapper(Class<T> mapperClass) {
        // 判断是否是接口类型
        if (mapperClass.isInterface()) {
            // 判断是否重复添加
            if (hasMapper(mapperClass)) {
                // 抛出异常
                throw new RuntimeException("类型 " + mapperClass + " mapperProxyFactoryMap已存在");
            }
            log.debug("添加的类：{}", mapperClass);
            // 注册映射器代理工厂
            mapperProxyFactoryMap.put(mapperClass, new MapperProxyFactory<>(mapperClass));
            log.debug("存放MapperProxyFactory的集合大小：{}", mapperProxyFactoryMap.size());
        }
    }

    /**
     * 判断mapperProxyFactoryMap中是否已经存在mapperClass
     *
     * @param mapperClass Mapper 接口类对象
     * @return 一个 boolean 类型的值，表示该 Mapper 接口是否已经被注册过
     */
    public  <T> boolean hasMapper(Class<T> mapperClass) {
        return mapperProxyFactoryMap.containsKey(mapperClass);
    }

    /**
     * 根据传入的 Mapper 接口类对象和 SqlSession 对象获取该 Mapper 接口的代理对象
     * @param mapperClass Mapper 接口类对象
     * @param sqlSession MyBatis SqlSession 对象
     * @return 一个泛型 T 类型的 Mapper 接口代理对象
     */
    public <T> T getMapper(Class<T> mapperClass, SqlSession sqlSession) {
        // 从 存放映射器代理的集合 根据key获取mapperProxyFactory对象
        // key--->类 value--->映射代理工厂
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) mapperProxyFactoryMap.get(mapperClass);
        log.debug("{}，\nDEBUG -{}，\nDEBUG -{}", mapperClass, sqlSession, mapperProxyFactory);
        if (mapperProxyFactory == null) {
            throw new RuntimeException(" 类型 " + mapperClass + "不存在MapperRegistry");
        }
        try {
            // 从 映射代理工厂 获取实例
            // mapperProxyFactory对象
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new RuntimeException("错误获取实例：" + e, e);
        }
    }


}
